package cn.matezk.lianlian.task;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.matezk.lianlian.beans.Constant;
import cn.matezk.lianlian.dao.DeviceDao;
import cn.matezk.lianlian.dao.NotifyRecordDao;
import cn.matezk.lianlian.dao.SecretKeyDao;
import cn.matezk.lianlian.dao.UserDao;
import cn.matezk.lianlian.framework.RedisMutexLock;
import cn.matezk.lianlian.model.Device;
import cn.matezk.lianlian.model.NotifyRecord;
import cn.matezk.lianlian.model.NotifyTemplate;
import cn.matezk.lianlian.model.SecretKey;
import cn.matezk.lianlian.service.INotifyService;
import cn.matezk.lianlian.utils.StringUtils;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.sdk.service.dyvmsapi20170525.AsyncClient;
import com.aliyun.sdk.service.dyvmsapi20170525.models.SingleCallByTtsRequest;
import com.aliyun.sdk.service.dyvmsapi20170525.models.SingleCallByTtsResponse;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Component
@Slf4j
public class NotifyRepeatTask {
    @Autowired
    private NotifyRecordDao notifyRecordDao;
    @Autowired
    private DeviceDao deviceDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private SecretKeyDao secretKeyDao;
    @Autowired
    private INotifyService notifyService;
    @Autowired
    private RedisMutexLock mutexLock;
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private Client client;
    @Autowired
    private AsyncClient asyncClient;

    @Value("${aliyun.tts.called-show-number}")
    private String showNumber;

    @XxlJob("notifyRepeatTask")
    public void execute() throws InterruptedException {
        List<NotifyRecord> records = notifyRecordDao.selectRepeatNotify();
        if (ObjectUtil.isNull(records)) return;
        for (NotifyRecord record : records) {
            // 是否绑定APPKEY
            SecretKey secretKey = secretKeyDao.deviceMatch(record.getProductId(), record.getDeviceName());
            // 是否绑定了用户
            Long userId = ObjectUtil.isNull(secretKey) ? null : secretKey.getBindUser();
            String amountLockKey = Device.createLockKey(record.getDeviceId());
            if (ObjectUtil.isNotNull(userId)) {
                amountLockKey = String.format("lock.user.%d", userId);
            }
            if (mutexLock.tryLock(amountLockKey, 10)) {
                try {
                    Device device = deviceDao.selectByPrimaryKey(record.getDeviceId());
                    int amount = device.getAmount();
                    if (ObjectUtil.isNotNull(userId)) {
                        // 用户余额
                        amount = userDao.userAmount(userId);
                    }
                    if (NotifyTemplate.METHOD_WECHAT.equals(record.getMethod())) {
                        // 公众号推送
                        wechatNotify(record);
                    } else if (NotifyTemplate.METHOD_SMS.equals(record.getMethod())) {
                        // 短信推送
                        amount = smsNotify(record, amount);
                    } else if (NotifyTemplate.METHOD_VOICE.equals(record.getMethod())) {
                        // 语音通知
                        amount = voiceNotify(record, amount);
                    }
                    // 修改报警金额
                    if (ObjectUtil.isNull(userId)) {
                        if (device.getAmount().intValue() != amount) {
                            deviceDao.updateAmount(record.getDeviceId(), amount);
                        }
                    } else {
                        userDao.updateAmount(userId, amount);
                    }
                } finally {
                    mutexLock.unlock(amountLockKey);
                }
            }
        }
    }

    private void wechatNotify(NotifyRecord record) {
        JSONObject pushParams = JSONUtil.parseObj(record.getParams());
        String url = pushParams.getStr("url");
        pushParams.remove(url);
        List<WxMpTemplateData> datas = new ArrayList<>();
        for (String key : pushParams.keySet()) {
            datas.add(new WxMpTemplateData(key, pushParams.getStr(key)));
        }
        WxMpTemplateMessage message = WxMpTemplateMessage.builder()
                .toUser(record.getToUser())
                .templateId(record.getTemplateCode())
                .data(datas)
                .url(url)
                .build();
        try {
            // 微信推送
            String bizId = wxMpService.getTemplateMsgService().sendTemplateMsg(message);
            record.setBizId(bizId);
            record.setStatus(NotifyRecord.STATUS_SUCCESS);
        } catch (WxErrorException e) {
            record.setStatus(NotifyRecord.STATUS_FAILED);
            record.setErrorCode(String.valueOf(e.getError().getErrorCode()));
            record.setErrorMsg(StringUtils.sub(e.getError().getErrorMsg(), NotifyRecord.ERROR_MSG_LEN));
            log.error(e.getMessage());
        } catch (Exception e) {
            record.setStatus(NotifyRecord.STATUS_FAILED);
            record.setErrorMsg(StringUtils.sub(e.getMessage(), NotifyRecord.ERROR_MSG_LEN));
        } finally {
            record.setNotifyTime(new Date());
            notifyRecordDao.updateByPrimaryKeySelective(record);
        }
    }

    private int smsNotify(NotifyRecord record, int amount) {
        if (amount >= Constant.SMS_UNIT_PRICE) {
            amount -= Constant.SMS_UNIT_PRICE;
        } else {
            record.setStatus(NotifyRecord.STATUS_FAILED);
            record.setErrorCode("AMOUNT_LESS");
            record.setErrorMsg("余额不足");
            notifyRecordDao.updateByPrimaryKeySelective(record);
            return amount;
        }
        try {
            SendSmsRequest request = new SendSmsRequest();
            request.setPhoneNumbers(record.getToUser());
            JSONObject params = JSONUtil.parseObj(record.getParams());
            request.setSignName(params.getStr("sign"));
            params.remove("sign");
            request.setTemplateCode(record.getTemplateCode());
            request.setTemplateParam(params.toString());
            request.setOutId("lianlian");
            SendSmsResponse response = client.sendSms(request);
            record.setBizId(response.getBody().getBizId());
            if (ObjectUtil.notEqual("OK", response.getBody().getCode())) {
                // 发送失败
                record.setStatus(NotifyRecord.STATUS_FAILED);
                record.setErrorCode(StringUtils.sub(response.getBody().getCode(), NotifyRecord.ERROR_CODE_LEN));
                record.setErrorMsg(StringUtils.sub(response.getBody().getMessage(), NotifyRecord.ERROR_MSG_LEN));
            } else {
                // 待回执
                record.setStatus(NotifyRecord.STATUS_CONFIRM);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            record.setStatus(NotifyRecord.STATUS_FAILED);
            record.setErrorMsg(StringUtils.sub(e.getMessage(), NotifyRecord.ERROR_MSG_LEN));
            amount += Constant.SMS_UNIT_PRICE;
        } finally {
            record.setNotifyTime(new Date());
            notifyRecordDao.updateByPrimaryKeySelective(record);
        }
        return amount;
    }

    private int voiceNotify(NotifyRecord record, int amount) {
        if (amount >= Constant.TTS_UNIT_PRICE) {
            amount -= Constant.TTS_UNIT_PRICE;
        } else {
            record.setStatus(NotifyRecord.STATUS_FAILED);
            record.setErrorCode("AMOUNT_LESS");
            record.setErrorMsg("余额不足");
            notifyRecordDao.updateByPrimaryKeySelective(record);
            return amount;
        }
        try {
            SingleCallByTtsRequest singleCallByTtsRequest = SingleCallByTtsRequest.builder()
                    .calledNumber(record.getToUser())
                    .calledShowNumber(showNumber)
                    .ttsCode(record.getTemplateCode())
                    .ttsParam(record.getParams())
                    .playTimes(3)
                    .volume(100)
                    .speed(5)
                    .outId("lianlian")
                    .build();
            CompletableFuture<SingleCallByTtsResponse> response = asyncClient.singleCallByTts(singleCallByTtsRequest);
            SingleCallByTtsResponse res = response.get();
            record.setBizId(res.getBody().getCallId());
            if ("OK".equals(res.getBody().getCode())) {
                record.setStatus(NotifyRecord.STATUS_CONFIRM);
            } else {
                record.setStatus(NotifyRecord.STATUS_FAILED);
                record.setErrorCode(StringUtils.sub(res.getBody().getCode(), NotifyRecord.ERROR_CODE_LEN));
                amount += Constant.TTS_UNIT_PRICE;
            }
        } catch (Exception e) {
            record.setStatus(NotifyRecord.STATUS_FAILED);
            record.setErrorMsg(StringUtils.sub(e.getMessage(), NotifyRecord.ERROR_MSG_LEN));
            amount += Constant.TTS_UNIT_PRICE;
        } finally {
            record.setNotifyTime(new Date());
            notifyRecordDao.updateByPrimaryKeySelective(record);
        }
        return amount;
    }
}
